package com.zx.controller;

import com.zx.base.shiro.MyShiroRealm;
import com.zx.pojo.User;
import com.zx.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.mgt.RealmSecurityManager;
import org.apache.shiro.subject.Subject;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.util.concurrent.TimeUnit;

@Controller
public class IndexController {
    @Resource
    private UserService userService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    //redis 中数据的 key 的前缀
    private String SHIRO_LOGIN_COUNT = "shiro_login_count_"; // 登录计数
    private String SHIRO_IS_LOCK = "shiro_is_lock_"; // 锁定用户登录

    @GetMapping("/login")
    public String toLogin() {
        return "login";
    }

    @PostMapping("/doLogin")
    public String doLogin(Model model, HttpSession session, String usrName, String usrPassword) {
        // 获取到对Redis的String操作对象
        ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();

        Subject subject = SecurityUtils.getSubject();
        long minutes;
        // 用户登录，成功返回当前用户信息
        try {
            UsernamePasswordToken token = new UsernamePasswordToken(usrName, usrPassword);
            subject.login(token); // 验证失败则抛出异常
        } catch (UnknownAccountException | IncorrectCredentialsException e) {
            // 如果该用户登录密码错误超过5次则锁定账号1小时

            // 每次访问，登录次数+1（ACID 减库存）
            opsForValue.increment(SHIRO_LOGIN_COUNT + usrName, 1); // shiro_login_count_test 1+1
            // 计数大于 5 时，设置用户被锁定一小时，清空登录计数
            if (Integer.parseInt(opsForValue.get(SHIRO_LOGIN_COUNT + usrName)) > 5) {
                opsForValue.set(SHIRO_IS_LOCK + usrName, "LOCK");
                // 设置锁定时间为 1 小时
                stringRedisTemplate.expire(SHIRO_IS_LOCK + usrName, 1, TimeUnit.HOURS);
                stringRedisTemplate.delete(SHIRO_LOGIN_COUNT + usrName); // 清空登录计数
            }
            // 判断用户是否被锁定
            if ("LOCK".equals(opsForValue.get(SHIRO_IS_LOCK + usrName))) {
                // throw new DisabledAccountException();
                // 获取Key的超时时间
                long expire = opsForValue.getOperations().getExpire(SHIRO_IS_LOCK + usrName);
                minutes = expire / 60;     //分钟
//                model.addAttribute("message", "尝试次数过多，请稍后之后再试！" + expire);
                model.addAttribute("message", "尝试次数过多,请在" + minutes + "分钟过后再试！");

                return "login";
            }
            model.addAttribute("message", "用户名或密码错误，登录失败！");
            return "login";
        } catch (LockedAccountException e) {
            model.addAttribute("message", "用户被禁用，登录失败！");
            return "login";
        } catch (AuthenticationException e) {
            model.addAttribute("message", "认证异常，登录失败！");
            return "login";
        }
        // 判断用户是否被锁定
        if ("LOCK".equals(opsForValue.get(SHIRO_IS_LOCK + usrName))) {
            // throw new DisabledAccountException();
            // 获取Key的超时时间
            long expire = opsForValue.getOperations().getExpire(SHIRO_IS_LOCK + usrName);
            //                model.addAttribute("message", "尝试次数过多，请稍后之后再试！" + expire);
            minutes = expire / 60;     //分钟
            model.addAttribute("message", "尝试次数过多,请在" + minutes + "分钟过后再试！");
            return "login";
        }
        User loginUser = (User) subject.getPrincipal();

        // 把用户保存到Session中
        session.setAttribute("loginUser", loginUser);

        //认证(登录)成功，清空登录计数
        stringRedisTemplate.delete(SHIRO_LOGIN_COUNT + usrName);

        // 清空权限缓存
        RealmSecurityManager rsm = (RealmSecurityManager) SecurityUtils.getSecurityManager();
        MyShiroRealm realm = (MyShiroRealm)rsm.getRealms().iterator().next();
        realm.clearMyCachedAuthorizationInfo();
        return "redirect:/main";
    }

    @GetMapping("/main")
    public String main() {
        return "main";
    }

    @GetMapping("/logout")
    public String doLogout(HttpSession session) {
        SecurityUtils.getSubject().logout();// Shiro 注销
        return "redirect:/login";
    }

    @RequestMapping(value = "/403")
    public String unauthorized() {
        return "403";
    }

}
